<?php
// +----------------------------------------------------------------------
// | Bwsaas
// +----------------------------------------------------------------------
// | Copyright (c) 2015~2020 http://www.buwangyun.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Gitee ( https://gitee.com/buwangyun/bwsaas )
// +----------------------------------------------------------------------
// | Author: buwangyun <hnlg666@163.com>
// +----------------------------------------------------------------------
// | Date: 2020-9-28 10:55:00
// +----------------------------------------------------------------------

namespace buwang\middleware;

use app\Request;

use buwang\interfaces\MiddlewareInterface;
use buwang\traits\JsonTrait;
use buwang\traits\JwtTrait;

use app\manage\model\Token;
use buwang\traits\JumpTrait;
use buwang\service\UserService;
use think\Exception;


/**
 * 系统权限访问管理
 * Class Auth
 * @package app\admin\middleware
 */
class Login implements MiddlewareInterface
{
    use JsonTrait;
    use JwtTrait;
    use JumpTrait;

    /**
     * @param Request $request
     * @param \Closure $next
     * @param bool $force
     * @return \buwang\traits\Respone|mixed|\think\response\Redirect
     */
    public function handle(Request $request, \Closure $next, bool $force = true)
    {
        $token = get_token($request);
        $code = 401;
        $err_msg = "未登录，请先去登录";
        $err_code = 400000;
        $user = false;
        if ($token) {
            //TODO: 20201112 jyk 不需要登录捕获422错误
            try{
                //已经登录
                $jwtinfo = self::decodeToken($token);//解析token,获取用户信息
            }catch (\Throwable $e){
                $token = null;//token解析报错算token失效,当做未登录处理
                $jwtinfo = null;
                //如果是需要强制登录的接口则异常422抛出去
                if($force)throw new \UnexpectedValueException($e->getMessage());
            }
            if($jwtinfo){
                //应用类型 mini_program app h5 official pc system
                $scopes = $jwtinfo->scopes;
                //redis验证
                $user = Token::validateRedisToken($token, $jwtinfo);
                $code = Token::getErrorCode() == 400001 ? 401 : 200;
                $err_code = Token::getErrorCode() ?: 400000;
                $err_msg = Token::getError("未登录，请先去登录");
            }
        }
        if (!$token || ($token && !$user)) {
            //未登录结果返回
            //此处处理前后端分离 获取scopes的情况
            $scopes = get_scopes($request);
            $user = false;
        }

        $node_name = '/' . app('http')->getName() . '/' . $request->rule()->getRoute();
        event('RequestStart', [$request, $scopes, $node_name, $user, $code, $err_msg, $err_code, $force]);
        //用户类型
        if (!$user && $force) {
            switch ($scopes) {
                case "admin":
                    //未登录结果返回
                    if ($request->isAjax()) {//ajax请求返回code
                        return $this->code($code)->error($err_msg, [], $err_code);
                    } else {//模板解析时重定向至登录页面
                        return $this->error_jump($err_msg, "/manage/admin/login");// return redirect('/manage/member.login');
                    }
                    break;
                case "member":
                    //未登录结果返回
                    if ($request->isAjax()) {//ajax请求返回code
                        return $this->code($code)->error($err_msg, [], $err_code);
                    } else {//模板解析时重定向至登录页面
                        return $this->error_jump($err_msg, "/manage/member/login");// return redirect('/manage/admin/login');
                    }
                    break;
                case "official":
                case "h5":
                    //未登录结果返回
                    if ($request->isAjax()) {//ajax请求返回code
                        return $this->code($code)->error($err_msg, [], $err_code);
                    } else {//模板解析时重定向至登录页面
                        $app_name = 'bwmall';
                        return $this->error_jump($err_msg, "/{$app_name}/user/login");// return redirect('/manage/admin/login');
                    }
                    break;
                case "mini_program":
                case "app":
                    //未登录结果返回
                    return $this->code($code)->error($err_msg, [], $err_code);
                    break;
                default:
                    return $this->code($code)->error($err_msg, [], $err_code);
            }
        }
        //挂载动态方法到 app\Request
        Request::macro('isUserLogin', function () use (&$user) {
            return $user && true;
        });
        Request::macro('userInfo', function () use (&$user) {
            return $user;
        });
        Request::macro('scopes', function () use (&$scopes) {
            return $scopes;
        });
        Request::macro('token', function () use (&$token) {
            return $token;
        });
        bind('bwRequest', 'app\Request');
        //跨scopes访问拒绝
        //上传接口不做scopes判断，可以达到应用通用使用
        if (!in_array($node_name, ['/manage/publicCommon/getUploadFiles', '/manage/publicCommon/upload', '/manage/publicCommon/uploadEditor', '/manage/publicCommon/uploadUeditor'])) {
            if (($scopes != get_scopes($request)) && $force) return $this->error_jump('请先退出当前已登录的后台，同时只能打开一个后台!',NOT_JUMP);
        }

        return $next($request);
    }
}
